#include "stdafx.h"

#include "TaskManager.h"

namespace Melting
{
namespace Parallel
{
	inline UBYTE* AlignedMalloc( UINT32 _iBlockSize, UINT32 _iAlignment )
	{
		UBYTE* pMemory = (UBYTE*) malloc( _iBlockSize + _iAlignment );

		UBYTE* pAlignedMemory = (UBYTE*)((UINT32)( pMemory + _iAlignment ) & ( ~ (_iAlignment - 1 ) ));

		// safe, because adding _iAlignment instead of (_iAlignment-1) guarantees it is past the actual start of the maloc'ed block.
		*(pAlignedMemory - 1) = pAlignedMemory - pMemory;

		return pAlignedMemory;
	}

	inline void AlignedFree( UBYTE* _pAlignedMemory )
	{
		UBYTE iOffset = *(_pAlignedMemory - 1);
		free( (void*)(_pAlignedMemory - iOffset) );
	}

	//--------------------------------------------------------------------------------------------------------------------
	class TaskWorker : public Thread
	{
	public:

		// CTOR
		TaskWorker	( void )
		: Thread()
		,	mID					( 0xFFFFFFFF )
		,	mAbortRequested		( 0 )
		//,	mMergeStatsRequested( 0 )
		{}

		// DTOR - clean thread termination
		~TaskWorker	( void )
		{
			mAbortRequested.Increment();
			Thread::Wait();
		}

		// Run the worker thread
		bool Run( TaskManager* _pManager )
		{
			return Thread::Run( & TaskWorker::Execute, _pManager );
		} 

		// 
		void SetID( UINT32 _iID )
		{
			mID = _iID;
		}

		//void RequestStatsMerging( void ) 
		//{
		//	mMergeStatsRequested.Increment();
		//}

 
	private:

		UINT32				mID;						///< ID of this worker thread 
		AtomicCounter		mAbortRequested;			///< Atomic used to request an abortion	
		//AtomicCounter		mMergeStatsRequested;		///< Atomic used to request the merging of profiling stats

		// Threaded function 
        static ThreadFunctionReturn     Execute( void* _pWorker )
		{
			PROFILING_INITIALIZE_TLS();


			TaskWorker* pWorker		= static_cast< TaskWorker* > ( _pWorker );
			TaskManager* pManager	= static_cast< TaskManager* > ( pWorker->GetData() );

			// vars used to minimize the access to the atomic used for abortion
			const INT32		LOOP_COUNT_BEFORE_EXTERNAL_CHECK = 100000;
			INT32			iLoopCnt	= 0;

			// AffinityID of this worker, used to grab task that are fitted to it
			UINT32			iAffinityID = pWorker->mID;

			// check for abortion request
			while( 0 == pWorker->mAbortRequested.Get() )
			{	
				// keep on working !
				while( iLoopCnt < LOOP_COUNT_BEFORE_EXTERNAL_CHECK )
				{
					TaskManager::TaskInfo* pTaskInfo = pManager->TakeNextReadyTask( iAffinityID );

					if( nullptr != pTaskInfo )
					{
						pTaskInfo->mTask.Execute();

						pManager->SetTaskDone( pTaskInfo );
					}
					else
					{	
						bool bSuccess = pManager->TryToSchedule();

						if( ! bSuccess )
						{
							pManager->WaitForAvailableTask();
							//// NDAL : dirty ?
							//Parallel::YieldProcessor();
							////Parallel::SwitchThread();
						}

	//					pManager->WaitForAvailableTask();
					}

					iLoopCnt++;
				}
				// reset 
				iLoopCnt = 0;

				//if( ENABLE_PROFILING )
				//{
				//	//-------------------------------------------------
				//	// When you want to process the stats (e.g. print) ,
				//	// or before leaving your sub-thread :
				//	//  first merge them [ from every thread (sub + main) ]
				//	if( pWorker->mMergeStatsRequested.Get() )
				//	{						
				//		Profiling::MergeTLSStats();
				//		mMergeStatsRequested.Set( 0 );
				//	} 
				//}
			}
			

			PROFILING_RELEASE_TLS();

			return (ThreadFunctionReturn)0;
		}

	}; 
	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------

	bool CompareTaskQueueInfos( const TaskQueueInfo& _rA, const TaskQueueInfo& _rB )
	{
		return _rA.miAffinityMask < _rB.miAffinityMask 
			|| ( ( _rA.miAffinityMask == _rB.miAffinityMask ) && _rA.miPriority > _rB.miPriority );
	}

	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	Task TaskManager::sEmptyTask;

	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	void TaskManager::DoNothing( const TaskFuncParams& _rParams )
	{
	}

	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	TaskManager::TaskManager( UINT32 _iNbWorkerThreads, UINT32 _iMaxTasks, UINT32 _iMaxPriority, UINT32 _iDefaultPriority, UINT32 _iNbTaskQueueInfos, TaskQueueInfo* _aTaskQueueInfos )
	:	miNbWorkers			( _iNbWorkerThreads )
	,	maWorkers			( nullptr )
	,	miNextTask			( 0 )
	,	miMaxTasks			( _iMaxTasks )
	,	maTaskInfos			( nullptr )
	,	miMaxPriority		( _iMaxPriority )
	,	miDefaultPriority	( _iDefaultPriority )
	{
		// create the task queue
		maTaskInfos = (TaskInfo*) AlignedMalloc( miMaxTasks * sizeof(TaskInfo), sizeof(TaskInfo) ); 
		//maTaskInfos = new TaskInfo[ miMaxTasks ];

		//maTaskInfos = maTaskInfos2;

		for( UINT32 i = 0; i < miMaxTasks; i++ )
		{
			maTaskInfos[ i ].mID			= i;	
			maTaskInfos[ i ].mParentID		= TaskManager::NO_TASK;		
			maTaskInfos[ i ].miPendingTasks.Set(0);
			maTaskInfos[ i ].mDependencyID	= TaskManager::NO_TASK;		
			maTaskInfos[ i ].miPriority		= miDefaultPriority;		
			maTaskInfos[ i ].miAffinityMask = 0;

		#ifndef __FINALROM		
			maTaskInfos[ i ].mstrDebugName		= 0;
		#endif
		}
 
		// create and run the workers
		maWorkers = new TaskWorker[ miNbWorkers ];

		for( UINT32 i = 0; i < miNbWorkers; i++ )
		{
			maWorkers[ i ].SetID( i );
		}

		// Init the global EmptyTask -- No need to bother with protection or multiple init !
		sEmptyTask.mFunc = TaskManager::DoNothing;
		//sEmptyTask.mParams.mData32[ 0 ] = 0;
		//sEmptyTask.mParams.mData32[ 1 ] = 0;
		//sEmptyTask.mParams.mData32[ 2 ] = 0;
		//sEmptyTask.mParams.mData32[ 3 ] = 0;

		//-----------------------------------------------------------
		#if TASK_MANAGER_WORKERS_SLEEP_WAIT
		{
			// No need to protect in the CTOR

			miTaskCount.Set( 0 ); 
		}
		#endif

		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCK_SINGLE_SHARED 
		{
			//mPendingQueue.reserve( miMaxTasks );
		}
		#endif

		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCK
		{ 
/*/
			mDependentTasks.reserve( _iMaxTasks ); 
/*/
			mNodeMemoryPool.Initialize( sizeof(LockFreeStack<TaskInfo*>::Node), _iMaxTasks, 0 ); 
//*/


			MB_ASSERT( 0 < _iNbTaskQueueInfos, "Cannot supply 0 TaskQueueInfo when using the lockfree implementation\n" );

			// create the requested queues
			miNbReadyQueues = _iNbTaskQueueInfos;
			maReadyQueues	= new TaskQueueSlot[ _iNbTaskQueueInfos ];
			
			// Sort the queue requests by affinity-priority
			std::sort( & _aTaskQueueInfos[ 0 ], & _aTaskQueueInfos[ _iNbTaskQueueInfos ], CompareTaskQueueInfos );

			UINT32 iCount = 0;

			UINT32 iTotalTaskCount = 0;

			for( UINT32 i = 0; i < _iNbTaskQueueInfos; i++ )
			{
				TaskQueueSlot slot;
				slot.mID.miPriority		= _aTaskQueueInfos[ i ].miPriority;
				slot.mID.miAffinityMask	= _aTaskQueueInfos[ i ].miAffinityMask;
				slot.mQueue.reserve( _aTaskQueueInfos[ i ].miMaxTasks );

				maReadyQueues[ i ] = slot;

				iTotalTaskCount += _aTaskQueueInfos[ i ].miMaxTasks;
			}

			MB_ASSERT( iTotalTaskCount <= miMaxTasks, "Insufficient 'MaxTaskCount' supplied.\n" );	
		}
		#endif
		
		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCKFREE
		{	
			mNodeMemoryPool.Initialize( sizeof(LockFreeStack<TaskInfo*>::Node), _iMaxTasks, 0 ); 

			MB_ASSERT( 0 < _iNbTaskQueueInfos, "Cannot supply 0 TaskQueueInfo when using the lockfree implementation\n" );

			// create the requested queues
			miNbReadyQueues = _iNbTaskQueueInfos;
			maReadyQueues	= new TaskQueueSlot[ _iNbTaskQueueInfos ];
			
			// Sort the queue requests by affinity-priority
			std::sort( & _aTaskQueueInfos[ 0 ], & _aTaskQueueInfos[ _iNbTaskQueueInfos ], CompareTaskQueueInfos );

			UINT32 iCount = 0;

			UINT32 iTotalTaskCount = 0;

			for( UINT32 i = 0; i < _iNbTaskQueueInfos; i++ )
			{
				TaskQueueSlot slot;
				slot.mID.miPriority		= _aTaskQueueInfos[ i ].miPriority;
				slot.mID.miAffinityMask	= _aTaskQueueInfos[ i ].miAffinityMask;
				slot.mpQueue			= new TaskQueue( _aTaskQueueInfos[ i ].miMaxTasks );

				maReadyQueues[ i ] = slot;

				iTotalTaskCount += _aTaskQueueInfos[ i ].miMaxTasks;
			}

			MB_ASSERT( iTotalTaskCount <= miMaxTasks, "Insufficient 'MaxTaskCount' supplied.\n" );	

		}//
		#endif		
		//-----------------------------------------------------------
		//-----------------------------------------------------------


		PROFILING_INITIALIZE( 1000000, miNbWorkers + 1 );
	}

	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	TaskManager::~TaskManager( void )
	{		
		PROFILING_RELEASE();

		//-----------------------------------------------------------
		#if TASK_MANAGER_WORKERS_SLEEP_WAIT
		{			
			// necessary to protect ?
			mMutexTaskPending.Lock();
			{
				// if 1 enough ? should we use miNbWorkerds instead ?
				miTaskCount.Set( 1 );
			}			
			mMutexTaskPending.Unlock();

			mConditionTaskPending.SignalAll();

		}
		#endif
		
		// NB : make sure to kill the workers BEFORE deleting anything they could touch
		// stop and destroy the workers
		delete [] maWorkers;
 
		AlignedFree( (UBYTE*) maTaskInfos );
		//delete [] maTaskInfos;		

		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCK
		{	 
			// delete all ready queues
			for( UINT32 i = 0; i < miNbReadyQueues; i++ )
			{
				maReadyQueues[ i ].mQueue.clear();
			}

			delete [] maReadyQueues ;			
		
/*/
			mDependentTasks.clear();		
/*/		
			mNodeMemoryPool.Release();
//*/		
		}
		#endif
		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCKFREE
		{	 
			// delete all ready queues
			for( UINT32 i = 0; i < miNbReadyQueues; i++ )
			{
				delete maReadyQueues[ i ].mpQueue;
			}

			delete [] maReadyQueues ;			
		
			mNodeMemoryPool.Release();
		}
		#endif
	}  
	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	bool TaskManager::Run( void )
	{
		bool bSuccess = true;

		for( UINT32 i = 0; i < miNbWorkers; i++ )
		{
			bool bRunSuccess = maWorkers[ i ].Run( this );
			bSuccess = bSuccess && bRunSuccess;

			MB_ERROR( bRunSuccess, "Could not properly run/create the worker thread %d\n", i );
		}  

		return bSuccess;
	}

	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	bool TaskManager::ForceSpecificThreadAffinities( void )
	{
		bool bSuccess = true;
		
		bool bAffinitySuccess;

		for( UINT32 i = 0; i < miNbWorkers; i++ )
		{
			bAffinitySuccess = maWorkers[ i ].SetAffinity( 1 << i );
			bSuccess = bSuccess && bAffinitySuccess;
			
			MB_ERROR( bAffinitySuccess, "Could not properly SetAffinity( %d ) on the worker thread %d\n", 1<<i, i );
			//LOG(" SetCurrentThreadAffinity %d success :%d\n", i, (int)bSuccess );
		}
 
		bAffinitySuccess = Parallel::Thread::SetCurrentThreadAffinity( 1 << ( miNbWorkers + 1 ) );
		bSuccess = bSuccess && bAffinitySuccess;
 
		MB_ERROR( bAffinitySuccess, "Could not properly SetAffinity( %d ) on the main thread\n", ( miNbWorkers + 1 ) );
		//LOG(" SetCurrentThreadAffinity %d success :%d\n", 1 << miNbWorkers + 1, (int)bSuccess );
 
		return bSuccess;
	}
	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	void TaskManager::WaitOnTask( TaskID _taskID, bool _bHelpOnWait )
	{
		TaskInfo* pWaitedTaskInfo = GetTaskInfo( _taskID );

		// arbitrarily defines the calling thread as 'the last' - cause for now in my client code the last slot is the general purpose one with no affinity... so FIXME someday
		UINT32 iAffinityID = miNbWorkers;

		// If the Task is not finished, wait for it by helping other Tasks 
		while( ! IsTaskDone( pWaitedTaskInfo ) )
		{
			if( _bHelpOnWait )
			{
				TaskInfo* pTaskInfo = TakeNextReadyTask( iAffinityID );		

				if( nullptr != pTaskInfo )
				{
					pTaskInfo->mTask.Execute();

					SetTaskDone( pTaskInfo );
				}
				else
				{		
					bool bSuccess = TryToSchedule();

					if( ! bSuccess )
					{
						// NDAL : dirty ?
//						Parallel::YieldProcessor();
						Parallel::SwitchThread();
					}
					Parallel::YieldProcessor();
//					Parallel::SwitchThread();
				}
			}
			else
			{
				bool bSuccess = TryToSchedule();

				if( ! bSuccess )
				{
					// NDAL : dirty ?
//					Parallel::YieldProcessor();
						Parallel::SwitchThread();
				}
			}
		}
	}


	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	TaskManager::TaskInfo*  TaskManager::TakeFreeTaskInfo( void )
	{
		TaskInfo*	pTaskInfo		= nullptr;
		TaskInfo*	pCandidate;
		INT32		iPendingTasks;

		INT32 iTryCount = miMaxTasks;		// max number of retry : we arbitrarily set it to max tasks

		INT32 iFreeTaskID;

		bool bFound = false;
		
		// take the next one in the array
		// make sure it is free ( i.e. open task count == 0 ),
		// else retry with the next, etc
		do 
		{ 
			iTryCount--;

			// NB : we will NEVER get iFreeTaskID == 0, since miNextTask.Increment() returns the result of the increment, and miNextTask is init as 0
			iFreeTaskID = miNextTask.Increment() % miMaxTasks;

			pCandidate = GetTaskInfo( iFreeTaskID );

			// Try see if this TaskInfo is available 
			if( 0 == pCandidate->miPendingTasks.Get() )
			{
				// Try to reserve it since it looks free 
				// so it means the reservation of this TaskInfo is a task in itself
				iPendingTasks = pCandidate->miPendingTasks.Increment();

				// 1 means this TaskInfo is used is a task in itself !
				if( 1 == iPendingTasks ) 
				{
					bFound = true;
				}
				// looks like someone was faster !
				else
				{
					// oops, remove the increment, since this TaskInfo is in used by someone else...
					pCandidate->miPendingTasks.Decrement();

					// and say "I'm sorry !"
				}
			}

			//// WRONG ! The code path related to this will cause TaskInfo not to be properly protected when taken with TakeFreeTaskInfo()
			//bFound = 0 == pCandidate->miPendingTasks.Get();
		}
		while( ! bFound && iTryCount > 0 );

	

		pTaskInfo = & maTaskInfos[ iFreeTaskID ];

		//-----------------
		// postcondition : FIXME : this ASSERT is invalid, since pTaskInfo will always be != nullptr
		MB_ASSERT( nullptr != pTaskInfo, "Could not find a free TaskInfo slot ! Your system is either buggy or too limited on its MaxTask count (%d) \n", miMaxTasks );
		//-----------------

		return pTaskInfo;
	}
	

	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	void TaskManager::SetTaskDone( TaskInfo* _pTaskInfo )
	{
		//-------------------
		// precondition
		MB_ASSERT( nullptr != _pTaskInfo, "Invalid TaskInfo ptr\n" );		
		//-------------------

		TaskInfo* pTaskInfo = _pTaskInfo;

		//LOG("SetTaskDone %d\n", pTaskInfo->mID );

		// Decrement the counter twice : once to say the TaskInfo is not in use anymore -- symetry of TakeFreeTaskInfo()
		pTaskInfo->miPendingTasks.Decrement(); 
		// and once again for the Task in itself...
		INT32 iPendingCount = pTaskInfo->miPendingTasks.Decrement(); 

		// this assert is not true any longer since we are calling Increment() from TakeFreeTaskInfo(), which can affect this test !
		MB_ASSERT( iPendingCount >= 0, "Invalid miOpenTasks counter : %d\n", iPendingCount );

		// LOG("Task done : %d - %s by %d \n", pTaskInfo->mID, pTaskInfo->mstrDebugName, Parallel::Thread::GetCurrentThreadID() );

		//-----------------------------------------------------------
		#if TASK_MANAGER_WORKERS_SLEEP_WAIT
		{
			// NO NEED to protect with a Lock here - no harmful logic is possible, it is only sensitive in our code when we increment the value

			// Global counter update
			miTaskCount.Decrement();
		}
		#endif


		TaskID parentID = pTaskInfo->mParentID;
		TaskID childID	= pTaskInfo->mID;

		if( NO_TASK != parentID )
		{ 				
			TaskInfo* pParentTaskInfo = GetTaskInfo( parentID );
			MB_ASSERT( nullptr != pParentTaskInfo, "Invalid Parent TaskID : %d ( Child Task is : %d ) \n", parentID, childID );
		
			//Parallel::MemoryBarrier(); 
			INT32 iCount = pParentTaskInfo->miPendingTasks.Decrement();	// DecrementRelease 
			MB_ASSERT( iCount >= 0, "Invalid miOpenTasks counter : %d ( on Parent TaskID %d of Child TaskID %d)\n", iCount, parentID, childID );		

		}
	} 

	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	TaskID TaskManager::RegisterTask( Task* _pTask, TaskID _parentID, TaskID _dependencyID, UINT32 _iPriority, UINT32 _iAffinityMask, UINT32 _iPendingTasks )
	{
		//--------------------
		// precondition
		MB_ASSERT( NO_TASK == _parentID || NO_TASK == _dependencyID || _parentID != _dependencyID, "A Task cannot depend on its parent !\n" );		
		//MB_ASSERT( miTaskCount.Get() < miMaxTasks , "TaskInfo buffer overlow ! Consider increasing the MaxTask for the TaskManager\n" );
		//--------------------

		// A. Get an available TaskInfo -------------------------------------------------------------------
		TaskInfo* pTaskInfo = TakeFreeTaskInfo(); 

		// B. Setup the TaskInfo -------------------------------------------------------------------
		// Dependency inherited from its parent, if any
		TaskID inheritedDependencyID = TaskManager::NO_TASK;

		// 1. Increase the openTask counter of its parent !		
		if( NO_TASK != _parentID )
		{
			TaskInfo* pParentInfo = GetTaskInfo( _parentID );

			MB_ASSERT( nullptr != pParentInfo, "Invalid Parent TaskID : %d ( Child Task is : %d ) \n", _parentID, pTaskInfo->mID );
			MB_ASSERT( 0 < pParentInfo->miPendingTasks.Get(), "Adding a Task to a Parent that is already done with its sub tasks ! Parent Task is %d ( Child Task is : %d ) \n", _parentID, pTaskInfo->mID );

			pParentInfo->miPendingTasks.Increment(); // Acquire ?

			// read only access should be safe
			inheritedDependencyID = pParentInfo->mDependencyID;
			
			//-----------------
			// paranoid
			MB_ASSERT( pParentInfo->miPendingTasks.Get() >= 0, "Invalid miOpenTasks counter : %d\n", pParentInfo->miPendingTasks.Get() );
			//-----------------
		}

		// 2. Fill in its informations
		// No need to lock since right now this Task is mine \o/ !
		UINT32 iTaskPriority	= ( TaskManager::UNDEFINED_PRIORITY == _iPriority ) ? miDefaultPriority		: _iPriority;
		TaskID iTaskDependency	= ( TaskManager::NO_TASK == _dependencyID )	? inheritedDependencyID : _dependencyID;
		{
			//LOG("RegisterTask %d\n", pTaskInfo->mID );

			// NB : if no task was specified, use the default empty task
			// NB : if no dependency was specified, we grab the one from the parent ! 
			// NB : if no priority was specified, use the default one !

			//// WRONG ! The code path related to this will cause TaskInfo not to be properly protected when taken with TakeFreeTaskInfo()
			// pTaskInfo->miPendingTasks.Set( _iPendingTasks ); // Acquire & Release

			// Add instead of Set because the TaskInfo slot reservation process already set it to 1 with a "successful" Increment() -- see TakeFreeTaskInfo()
			pTaskInfo->miPendingTasks.Add( _iPendingTasks ); // Acquire & Release

			pTaskInfo->mTask			= ( nullptr != _pTask ) ? *_pTask : sEmptyTask ;		//  FIXME ?
			pTaskInfo->mParentID		= _parentID;
			pTaskInfo->mDependencyID	= iTaskDependency;
			pTaskInfo->miPriority		= iTaskPriority;
			pTaskInfo->miAffinityMask	= _iAffinityMask;
		}
	

		// C. Register it for scheduling -------------------------------------------------------------------

		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCK_SINGLE_SHARED
		{
			mMutex.Lock();
			mPendingQueue.push_back( pTaskInfo->mID );
			mMutex.Unlock();
		}
		#endif


		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCK
		{
			// handle dependency
			if( IsReadyToStart( pTaskInfo ) )
			{
				// put it in the appropriate ready queue
				// TODO : Use safe & faster access container !
				AddToReadyQueue( pTaskInfo );
			}
			else
			{
				AddToWaitQueue( pTaskInfo );
			}
		}
		#endif 

		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCKFREE
		{
			// handle dependency
			if( IsReadyToStart( pTaskInfo ) )
			{
				// put it in the appropriate ready queue
				// TODO : Use safe & faster access container !
				AddToReadyQueue( pTaskInfo );
			}
			else
			{
				AddToWaitQueue( pTaskInfo );
			}
		}
		#endif
		
		//-----------------------------------------------------------
		#if TASK_MANAGER_WORKERS_SLEEP_WAIT
		{
			// Global counter update

			mMutexTaskPending.Lock();
			{
				miTaskCount.Increment();
			}
			mMutexTaskPending.Unlock();

			mConditionTaskPending.SignalAll();
			
//			mMutexTaskPending.Unlock();

			/*if( 1 == _iPendingTasks )
			{
				mConditionTaskPending.SignalOne();
			}
			else
			{
				mConditionTaskPending.SignalAll();
			}*/
		}
		#endif


		return pTaskInfo->mID;
	}

//	//--------------------------------------------------------------------------------------------------------------------
//	//--------------------------------------------------------------------------------------------------------------------
//	void TaskManager::WaitForAvailableTask( void )
//	{	
//		#if TASK_MANAGER_WORKERS_SLEEP_WAIT
//		{
//			mMutexTaskPending.Lock();
//
//			while( 0 == miTaskCount.Get() ) 
//			{
//				mConditionTaskPending.Wait( mMutexTaskPending );
//			}
//
//			mMutexTaskPending.Unlock();
//		}
//		#else
//		{ 
//			Parallel::YieldProcessor();
//		}
//		#endif
//	}
	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	TaskManager::TaskInfo*  TaskManager::TakeNextReadyTask( UINT32 _iAffinityID )
	{ 
		TaskManager::TaskInfo* pTaskInfo = nullptr; 
			
		UINT32 iAffinityBitSet = 1 << _iAffinityID; 

		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCK_SINGLE_SHARED
		{
			mMutex.Lock();
			{ 
				for( std::vector<TaskID>::iterator it = mPendingQueue.begin(); it != mPendingQueue.end(); it++ )
				{
					TaskID taskID = *it;
					TaskInfo* pCandidateTaskInfo = GetTaskInfo( taskID );

					MB_ASSERT( pCandidateTaskInfo->miPendingTasks.Get() > 0, "Task processed but not properly removed from the Pending list\n" );
		
					// if there is NO dependency... OR if the dependency has been completed
					if(	NO_TASK == pCandidateTaskInfo->mDependencyID	||	IsTaskDone( GetTaskInfo( pCandidateTaskInfo->mDependencyID ) )  )
					{
						// if the affinityBitSet of the requester matches the affinityMask of the Task
						if( pCandidateTaskInfo->miAffinityMask & iAffinityBitSet )
						{	
							//LOG("\n\nGrabbed TaskInfo ( affinityID = %d ) by Thread %d: \n"\
							//	"task.mstrName %s\n"\
							//	"task.mID %d\n"\
							//	"task.miAffinityMask %d\n"\
							//	"task.mDependencyID %d\n"\
							//	"dependent_task.miPendingTasks %d\n\n"
							//	, _iAffinityID
							//	, Parallel::Thread::GetCurrentThreadID()
							//	, ( 0 != pCandidateTaskInfo->mstrDebugName ) ? pCandidateTaskInfo->mstrDebugName : "no_name"
							//	, pCandidateTaskInfo->mID
							//	, pCandidateTaskInfo->miAffinityMask
							//	, pCandidateTaskInfo->mDependencyID
							//	, ( pCandidateTaskInfo->mDependencyID != NO_TASK ) ? GetTaskInfo( pCandidateTaskInfo->mDependencyID )->miPendingTasks.Get() : 0
							//	  );

							// then we can take this task !
							mPendingQueue.erase( it );
							pTaskInfo = pCandidateTaskInfo;
							break;
						} 
					}
				}
			}
			mMutex.Unlock(); 
		}
		#endif  
 
		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCK
		{ 
			// search the ready queues for one that match the given _affinity
			// first are specific queues, then the last one is the global one
			// queues are also sorted in decreasing priorities order
			for( UINT32 i = 0; i < miNbReadyQueues && nullptr == pTaskInfo; i++ )
			{
				if( iAffinityBitSet & maReadyQueues[ i ].mID.miAffinityMask )
				{
					maReadyQueues[ i ].mLock.Lock(); // Read lock is enough
					{
						if( maReadyQueues[ i ].mQueue.size() > 0 )
						{
							TaskQueue::iterator it = maReadyQueues[ i ].mQueue.begin();

							TaskInfo* pDequeued = (*it);

							if( nullptr != pDequeued )
							{
								// OK ! we are done
								pTaskInfo = pDequeued;							
								maReadyQueues[ i ].mQueue.erase( it ); 
							}
							// else we have just been stolen the last item from this queue, so go to the next queue
						}
					}					
					maReadyQueues[ i ].mLock.Unlock();
				}
			}
		}
		#endif 
		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCKFREE
		{  
			// search the ready queues for one that match the given _affinity
			// first are specific queues, then the last one is the global one
			// queues are also sorted in decreasing priorities order
			for( UINT32 i = 0; i < miNbReadyQueues; i++ )
			{
				if( iAffinityBitSet & maReadyQueues[ i ].mID.miAffinityMask )
				{
					if( maReadyQueues[ i ].mpQueue->GetSize() > 0 )
					{
						TaskInfo* pDequeued = maReadyQueues[ i ].mpQueue->Dequeue();

						if( nullptr != pDequeued )
						{
							// OK ! we are done
							pTaskInfo = pDequeued;
							break;
						}
						// else we have just been stolen the last item from this queue, so go to the next queue
					}
				}
			}
		}
		#endif
		

		return pTaskInfo; 
	}

	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	void TaskManager::AddToReadyQueue( TaskInfo* _pTaskInfo )
	{
		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCK
		{ 
			TaskQueueID queueID;
			queueID.miPriority		= _pTaskInfo->miPriority;
			queueID.miAffinityMask	= _pTaskInfo->miAffinityMask;

			UINT32 iSlot = 0xFFFFFFFF;
 
			// TODO : optimize this search with a constant time accessor
			for( UINT32 i = 0; i < miNbReadyQueues; i++ )
			{
				if( maReadyQueues[ i ].mID.miKey == queueID.miKey )
				{
					iSlot = i;
					break;
				}
			}
			
			MB_ASSERT( 0xFFFFFFFF != iSlot, "Appropriate ready Queue not found\n" );  
			
			maReadyQueues[ iSlot ].mLock.Lock();
			{
				maReadyQueues[ iSlot ].mQueue.push_back( _pTaskInfo );
			}
			maReadyQueues[ iSlot ].mLock.Unlock();

		}
		#endif 
		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCKFREE
		{ 
			TaskQueueID queueID;
			queueID.miPriority		= _pTaskInfo->miPriority;
			queueID.miAffinityMask	= _pTaskInfo->miAffinityMask;

			UINT32 iSlot = 0xFFFFFFFF;

			/*
			if( TaskManager::UNDEFINED_AFFINITY == _pTaskInfo->miAffinityMask )
			{
				iSlot = maReadyQueues.size() - 1;
			}
			else
			{
				iSlot = FindLSB( _pTaskInfo->miAffinityMask ) + 1;
			}
			*/
			// TODO : optimize this search with a constant time accessor
			for( UINT32 i = 0; i < miNbReadyQueues; i++ )
			{
				if( maReadyQueues[ i ].mID.miKey == queueID.miKey )
				{
					iSlot = i;
					break;
				}
			}
			
			MB_ASSERT( 0xFFFFFFFF != iSlot, "Appropriate ready Queue not found\n" );  

			bool bEnqueueSuccess;
			do
			{
				bEnqueueSuccess = maReadyQueues[ iSlot ].mpQueue->Enqueue( _pTaskInfo );
			}
			while( !bEnqueueSuccess ); 		
		}
		#endif 
	}

	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	void TaskManager::AddToWaitQueue( TaskInfo* _pTaskInfo )	
	{
		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCK
		{ 
/*/
			mLockDependencies.Lock();
			{
				mDependentTasks.push_back( _pTaskInfo ); 
			}
			mLockDependencies.Unlock();
/*/			
			LockFreeStack<TaskInfo*>::Node* pNode = (LockFreeStack<TaskInfo*>::Node*) mNodeMemoryPool.Allocate(); 
			pNode->mValue = _pTaskInfo;

//			mLockDependencies.Lock();
			{
				mDependentTasks.Push( pNode ); 
			}
//			mLockDependencies.Unlock();
//*/
		}
		#endif 
		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCKFREE
		{ 
			LockFreeStack<TaskInfo*>::Node* pNode = (LockFreeStack<TaskInfo*>::Node*) mNodeMemoryPool.Allocate(); 
			pNode->mValue = _pTaskInfo;

			mDependentTasks.Push( pNode ); 
		}
		#endif 
	}

	//--------------------------------------------------------------------------------------------------------------------
	//--------------------------------------------------------------------------------------------------------------------
	bool TaskManager::TryToSchedule( void )
	{	
		bool bScheduling;

		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCK_SINGLE_SHARED
		{ 
			bScheduling = false;
		}
		#endif
		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCK
		{ 
			bScheduling = mLockScheduling.TryLock();

			if( bScheduling )
			{
/*/
				mLockDependencies.Lock();	// Read Lock should be enough

				if( 0 < mDependentTasks.size() )
				{
					std::vector<TaskInfo*>		aDependentTasks = mDependentTasks;
					mDependentTasks.clear();

					mLockDependencies.Unlock();	// release for now

					// remember the last taskID that we found out was "done" - this is to minimize cache flushes
					TaskID lastTaskDoneID = NO_TASK;

					UINT32 iListSize = 0;
					
					//std::vector<TaskInfo*>		aRemainingTasks;
					//aRemainingTasks.reserve( aDependentTasks.size() );

					for( UINT32 i = 0; i < aDependentTasks.size(); i++ )
					{ 	 
						// Get the TaskInfo to check
						TaskInfo* pTaskInfo = aDependentTasks[ i ];

						// If the dependency is gone, add this task to the ready queue
						if(		lastTaskDoneID == pTaskInfo->mDependencyID 
							||	IsTaskDone( GetTaskInfo( pTaskInfo->mDependencyID ) )  )
						{
							lastTaskDoneID = pTaskInfo->mDependencyID;
							
							// put it in the appropriate ready queue
							AddToReadyQueue( pTaskInfo ); 
						}
						// else remember the current node as the 'last valid' in the list
						else
						{ 
//							aRemainingTasks.push_back( pTaskInfo );
							mLockDependencies.Lock();
							{
								mDependentTasks.push_back( pTaskInfo );
							}
							mLockDependencies.Unlock(); 
						}
					}

					//mLockDependencies.Lock();
					//{
					//	mDependentTasks.insert( mDependentTasks.end(), aRemainingTasks.begin(), aRemainingTasks.end() );
					//}
					//mLockDependencies.Unlock(); 



				}
				else
				{ 				
					mLockDependencies.Unlock();
				}
				
/*/
//				mLockDependencies.Lock();	// Read Lock should be enough

				if( 0 < mDependentTasks.GetSize() )
				{
					LockFreeStack<TaskInfo*>::Node* pFirstNode	= mDependentTasks.DetachAll();

//					mLockDependencies.Unlock();	// release for now

					LockFreeStack<TaskInfo*>::Node* pLastNode	= pFirstNode;

					LockFreeStack<TaskInfo*>::Node* pNode = pFirstNode;
					LockFreeStack<TaskInfo*>::Node* pNext = nullptr;

					// remember the last taskID that we found out was "done" - this is to minimize cache flushes
					TaskID lastTaskDoneID		= NO_TASK;
					TaskID lastTaskNotDoneID	= NO_TASK;

					UINT32 iListSize = 0;

					while( nullptr != pNode )
					{ 	
						// prepare the next node
						pNext = pNode->mpNext;

						// Get the TaskInfo to check
						TaskInfo* pTaskInfo = pNode->mValue;				

						// If the dependency is gone, add this task to the ready queue
						if(		lastTaskDoneID == pTaskInfo->mDependencyID 
							||	(
									lastTaskNotDoneID != pTaskInfo->mDependencyID &&
									IsTaskDone( GetTaskInfo( pTaskInfo->mDependencyID ) )  
								)
							)
						{
							lastTaskDoneID = pTaskInfo->mDependencyID;
							
							// put it in the appropriate ready queue
							AddToReadyQueue( pTaskInfo );
							// ----------------------------------------------------

							if( pFirstNode == pNode )
							{
								pFirstNode = pNext;
							}

							// Free the Node and go to the next
							mNodeMemoryPool.Free( (UBYTE*) pNode );
						}
						// else remember the current node as the 'last valid' in the list
						else
						{
							lastTaskNotDoneID = pTaskInfo->mDependencyID;

							// save a pointer to the last visited node that remains in the list
							pLastNode = pNode;

							iListSize++;

							//mLockDependencies.Lock();	// take 
							//{
							//	mDependentTasks.Push( pNode );
							//}
							//mLockDependencies.Unlock();	// release 
						}

						// go to the next
						pNode = pNext;
					} 

					if( 0 < iListSize )
					{
						//mLockDependencies.Lock();	// take 
						{
							mDependentTasks.PushList( pFirstNode, pLastNode, iListSize );
						}
						//mLockDependencies.Unlock();	// release 
					} 
				}
				else
				{ 				
					//mLockDependencies.Unlock();
				}

//*/

				mLockScheduling.Unlock();
			}
		}
		#endif
		//-----------------------------------------------------------
		//-----------------------------------------------------------
		#if TASK_MANAGER_IMPL_LOCKFREE
		{ 
			bScheduling = mLockScheduling.TryLock();

			if( bScheduling )
			{
				if( 0 < mDependentTasks.GetSize() )
				{
					LockFreeStack<TaskInfo*>::Node* pFirstNode	= mDependentTasks.DetachAll();
					LockFreeStack<TaskInfo*>::Node* pLastNode	= pFirstNode;

					LockFreeStack<TaskInfo*>::Node* pNode = pFirstNode;
					LockFreeStack<TaskInfo*>::Node* pNext = nullptr;

					// remember the last taskID that we found out was "done" - this is to minimize cache flushes
					TaskID lastTaskDoneID		= NO_TASK;
					TaskID lastTaskNotDoneID	= NO_TASK;

					UINT32 iListSize = 0;

					while( nullptr != pNode )
					{ 	
						// prepare the next node
						pNext = pNode->mpNext;

						// Get the TaskInfo to check
						TaskInfo* pTaskInfo = pNode->mValue;				

						// If the dependency is gone, add this task to the ready queue
						if(		lastTaskDoneID == pTaskInfo->mDependencyID 
							||	( 
//									lastTaskNotDoneID != pTaskInfo->mDependencyID &&			// try to avoid cache misses									
									IsTaskDone( GetTaskInfo( pTaskInfo->mDependencyID ) )
								)
							)
						{
							lastTaskDoneID = pTaskInfo->mDependencyID;
							
							// put it in the appropriate ready queue
							AddToReadyQueue( pTaskInfo );
							// ----------------------------------------------------

							if( pFirstNode == pNode )
							{
								pFirstNode = pNext;
							}

							// Free the Node and go to the next
							mNodeMemoryPool.Free( (UBYTE*) pNode );
						}
						// else remember the current node as the 'last valid' in the list
						else
						{
//							lastTaskNotDoneID = pTaskInfo->mDependencyID;

							// save a pointer to the last visited node that remains in the list
							pLastNode = pNode;
							iListSize++;

							//mDependentTasks.Push( pNode );

						}

						// go to the next
						pNode = pNext;
					}

					if( 0 < iListSize )
					{
						mDependentTasks.PushList( pFirstNode, pLastNode, iListSize );
					}
				}

				mLockScheduling.Unlock();
			}
		}
		#endif 

		return bScheduling;
	} 


}
}// namespace Melting